package com.univrz.flexmachine.core
{
	
	
	
	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;
	
	import mx.core.IMXMLObject;
	import mx.logging.ILogger;
	import mx.logging.Log;



	[DefaultProperty("children")]
	/**
	 * 
	 * The base class of all the hierarchical state machine classes.
	 * We know that the hierarchy of a state machine is not changed once it is compiled(),
	 * so we do some caching inside.
	 * Don't change things at runtime!
	 * ( it would be against the semantics of a state machine anyway ).
	 * 
	 * @author abucchi
	 * 
	 */
	public class Node extends EventDispatcher implements IMXMLObject
	{
		
		
		/**
		 * Subclasses set their own loggers
		 */		
		protected var logger:ILogger;
		
		
		
		
		/**
		 * Useful to write inline documentation.
		 * It will also be used to compose debugging messages.
		 */		
		public var comment:String;
		
		
		
		
		
		
		
		
		
		
		
		////////////////////////////////////////////////////////////////
		// parent 
		////////////////////////////////////////////////////////////////
		
		private var _parent:Node;

		public function get parent( ):Node
		{
			return _parent;
		}
		
		
		
		
		////////////////////////////////////////////////////////////////
		// children 
		////////////////////////////////////////////////////////////////
		
		private var _children:Array = [];
		
		// the ArrayElementType is overriden by subclasses to disable
		// invalid nesting configurations in MXML
		[ArrayElementType("com.univrz.flexmachine.core.Node")]		
		
		public function set children( v:Array ):void
		{
			if ( _children == v )
				return;
				
			for each ( var child:Node in v )
			{
				// add a reference to parent ( us )
				child._parent = this;
			}	
				
			_children = v;
		}
		public function get children( ):Array
		{
			return _children;
		}
		
		
		
		


		////////////////////////////////////////////////////////////////
		// getChildrenOfType 
		////////////////////////////////////////////////////////////////		
		
		public function getChildrenOfType( type:Class ):Array
		{
			var result:Array = [];
			for each ( var child:Object in _children )
				if ( child is type )
					result.push( child );
			return result;
		}
		
				
				
				
		////////////////////////////////////////////////////////////////
		// ancestors
		////////////////////////////////////////////////////////////////
		
		private var _ancestors:Array;
		
		public function get ancestors( ):Array
		{
			if ( !_ancestors )
			{
				_ancestors = [];
				if ( parent != null )
					_ancestors = _ancestors.concat( parent.ancestors );			
			}
			return _ancestors;
		}
		
		
		
		
		
		
		////////////////////////////////////////////////////////////////
		// descendants
		////////////////////////////////////////////////////////////////		
		
		private var _descendants:Array;
		
		public function get descendants( ):Array
		{
			if ( !_descendants )
			{
				_descendants = [];
				for each ( var child:Node in children )
				{
					_descendants.push( child );
					_descendants = _descendants.concat( child.descendants );			
				}
			}
			return _descendants;
		}		
		
		








		////////////////////////////////////////////////////////////////
		// getLCA
		////////////////////////////////////////////////////////////////

		/**
		 * 
		 * Returns the least common ancestor between this node and the other, if any
		 * 
		 * @param otherNode
		 * @return 
		 * 
		 */		
		public function getLCA( otherNode:Node ):Node
		{		
			if ( _getLCA[otherNode] === undefined )
			{
				var n:Node = $getLCA( otherNode );
				_getLCA[otherNode] = n;
				return n;
			}
			return _getLCA[otherNode];
		}
		
		private var _getLCA:Dictionary = new Dictionary( ); // why make it weak if the complete state machine works as a block 
				 
		private function $getLCA( otherNode:Node ):Node
		{
		
			if ( this == otherNode )	// border case
				return this;
			
			var acs:Array = [this].concat( ancestors );	// our ancestors including us
		
			var p:Node = otherNode;

			do
			{
				if ( acs.indexOf( p ) != -1 )
					return p;			
			}
			while ( p = otherNode.parent )
			
			return null;
		
		}
		
		
		

		////////////////////////////////////////////////////////////////
		// setup( )
		////////////////////////////////////////////////////////////////

		/**
		 * 
		 * Recursively compiles the state machine and sets up listeners, starting at the root.
		 * This method must be called at the root state, which must have at least one child.
		 * It will perform some structural checking
		 * 
		 */
		public function setup( ):void
		{
		
		
		}
		

		
		protected function internalSetup( ):void
		{
		
		
		}




		
		
		////////////////////////////////////////////////////////////////
		// isDescendantOf
		////////////////////////////////////////////////////////////////		

		/**
		 * 
		 * @param possibleAncestor
		 * @return 
		 * 
		 */		
		public function isDescendantOf( possibleAncestor:Node ):Boolean
		{
			return ( ancestors.indexOf( possibleAncestor ) != -1 );
		}
		

		
		
		
		////////////////////////////////////////////////////////////////
		// IMXMLObject 
		////////////////////////////////////////////////////////////////
		
		[Bindable]
		public var id:String;
		
		public function initialized( document:Object, id:String ):void
		{
			this.id = id;
		}





		////////////////////////////////////////////////////////////////
		// Utils
		///////////////////////////////////////////////////////////////

		protected static function getStandardLogger( instanceOrClass:Object ):ILogger
		{
			var category:String = getQualifiedClassName( instanceOrClass );
			category = category.split("::").join(".");
			return Log.getLogger( category );
		}






		////////////////////////////////////////////////////////////////
		// Visual properties ( not used, but needed for V support ) TODO: explain this
		///////////////////////////////////////////////////////////////

		public var x:int;
		public var y:int;
		public var width:int;
		public var height:int;
		public var layout:String;
		public var title:String;






		
	}
}